import Map, { CreateCallback } from "./map.js";
import RNG from "../rng.js";

/**
 * Icey's Maze generator
 * See http://roguebasin.com/index.php/Simple_maze for explanation
 */
export default class IceyMaze extends Map {
	_regularity: number;
	_map: number[][];

	constructor(width: number, height: number, regularity = 0) {
		super(width, height);
		this._regularity = regularity;
		this._map = [];
	}

	create(callback: CreateCallback) {
		let width = this._width;
		let height = this._height;
		let map = this._fillMap(1);
		
		width -= (width % 2 ? 1 : 2);
		height -= (height % 2 ? 1 : 2);

		let cx = 0;
		let cy = 0;
		let nx = 0;
		let ny = 0;

		let done = 0;
		let blocked = false;
		let dirs = [
			[0, 0],
			[0, 0],
			[0, 0],
			[0, 0]
		];
		do {
			cx = 1 + 2*Math.floor(RNG.getUniform()*(width-1) / 2);
			cy = 1 + 2*Math.floor(RNG.getUniform()*(height-1) / 2);

			if (!done) { map[cx][cy] = 0; }
			
			if (!map[cx][cy]) {
				this._randomize(dirs);
				do {
					if (Math.floor(RNG.getUniform()*(this._regularity+1)) == 0) { this._randomize(dirs); }
					blocked = true;
					for (let i=0;i<4;i++) {
						nx = cx + dirs[i][0]*2;
						ny = cy + dirs[i][1]*2;
						if (this._isFree(map, nx, ny, width, height)) {
							map[nx][ny] = 0;
							map[cx + dirs[i][0]][cy + dirs[i][1]] = 0;
							
							cx = nx;
							cy = ny;
							blocked = false;
							done++;
							break;
						}
					}
				} while (!blocked);
			}
		} while (done+1 < width*height/4);
		
		for (let i=0;i<this._width;i++) {
			for (let j=0;j<this._height;j++) {
				callback(i, j, map[i][j]);
			}
		}
		this._map = [];
		return this;
	}

	_randomize(dirs: number[][]) {
		for (let i=0;i<4;i++) {
			dirs[i][0] = 0;
			dirs[i][1] = 0;
		}
		
		switch (Math.floor(RNG.getUniform()*4)) {
			case 0:
				dirs[0][0] = -1; dirs[1][0] = 1;
				dirs[2][1] = -1; dirs[3][1] = 1;
			break;
			case 1:
				dirs[3][0] = -1; dirs[2][0] = 1;
				dirs[1][1] = -1; dirs[0][1] = 1;
			break;
			case 2:
				dirs[2][0] = -1; dirs[3][0] = 1;
				dirs[0][1] = -1; dirs[1][1] = 1;
			break;
			case 3:
				dirs[1][0] = -1; dirs[0][0] = 1;
				dirs[3][1] = -1; dirs[2][1] = 1;
			break;
		}
	}

	_isFree(map: number[][], x: number, y: number, width: number, height: number) {
		if (x < 1 || y < 1 || x >= width || y >= height) { return false; }
		return map[x][y];
	}
}
